package com.summer.framework.redis.service;

import com.fasterxml.jackson.core.type.TypeReference;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.summer.framework.redis.script.ScriptConstant;
import org.springframework.data.redis.connection.RedisConnectionFactory;
import org.springframework.data.redis.core.*;
import org.springframework.data.redis.support.atomic.RedisAtomicLong;
import org.springframework.stereotype.Component;
import org.springframework.util.StringUtils;

import java.time.Duration;
import java.time.LocalDate;
import java.time.format.DateTimeFormatter;
import java.util.*;
import java.util.concurrent.TimeUnit;

/**
 * Description: redis服务默认实现类
 * @auther Ice Yang
 * Date 2025/7/2
 */
@Component
public class DefaultRedisService implements RedisService{

    private static final String DEFAULT_PREFIX = "SM";

    private final RedisTemplate<String, Object> template;
    private final ObjectMapper objectMapper;

    // 不同数据结构的操作对象
    private final ValueOperations<String, Object> valueOps;
    private final ListOperations<String, Object> listOps;
    private final SetOperations<String, Object> setOps;
    private final HashOperations<String, String, Object> hashOps;

    public DefaultRedisService(RedisTemplate<String, Object> redisTemplate, ObjectMapper objectMapper) {
        this.template = redisTemplate;
        this.objectMapper = objectMapper;
        // 初始化操作对象
        this.valueOps = redisTemplate.opsForValue();
        this.listOps = redisTemplate.opsForList();
        this.setOps = redisTemplate.opsForSet();
        this.hashOps = redisTemplate.opsForHash();
    }

    /********************** 通用操作 **********************/

    /**
     * 设置键的过期时间
     * @param key 键
     * @param timeout 时间
     * @param unit 时间单位
     */
    @Override
    public void expire(String key, long timeout, TimeUnit unit) {
        template.expire(key, timeout, unit);
    }

    /**
     * 设置键的过期时间（Duration方式）
     * @param key 键
     * @param duration 持续时间
     */
    @Override
    public void expire(String key, Duration duration) {
        template.expire(key, duration);
    }

    /**
     * 删除键
     * @param key 键
     * @return 是否删除成功
     */
    @Override
    public Boolean delete(String key) {
        return template.delete(key);
    }

    /**
     * 批量删除键
     * @param keys 键集合
     * @return 删除的数量
     */
    @Override
    public Long delete(Collection<String> keys) {
        return template.delete(keys);
    }

    /**
     * 检查键是否存在
     * @param key 键
     * @return 是否存在
     */
    @Override
    public Boolean hasKey(String key) {
        return template.hasKey(key);
    }

    /**
     * 获取剩余过期时间
     * @param key 键
     * @return 剩余时间（秒）
     */
    @Override
    public Long getExpire(String key) {
        return template.getExpire(key);
    }

    /**
     * 存储单个对象
     * @param key 键
     * @param value 值
     */
    @Override
    public void setObject(String key, Object value) {
        valueOps.set(key, value);
    }

    /**
     * 存储单个对象并设置过期时间
     * @param key 键
     * @param value 值
     * @param timeout 过期时间
     * @param unit 时间单位
     */
    @Override
    public void setObject(String key, Object value, long timeout, TimeUnit unit) {
        valueOps.set(key, value, timeout, unit);
    }

    /**
     * 获取单个对象
     * @param key 键
     * @return 对象
     */
    @Override
    public Object getObject(String key) {
        return valueOps.get(key);
    }

    /**
     * 获取指定类型的对象
     * @param key 键
     * @param clazz 对象类型
     * @return 对象
     */
    @Override
    public <T> T getObject(String key, Class<T> clazz) {
        Object obj = valueOps.get(key);
        return convertValue(obj, clazz);
    }

    /**
     * 获取复杂泛型对象
     * @param key 键
     * @param typeReference 类型引用
     * @return 对象
     */
    @Override
    public <T> T getObject(String key, TypeReference<T> typeReference) {
        Object obj = valueOps.get(key);
        return objectMapper.convertValue(obj, typeReference);
    }

    /**
     * 更新对象
     * @param key 键
     * @param updateFunction 更新函数
     * @return 更新后的对象
     */
    @Override
    public <T> T updateObject(String key, Class<T> clazz, java.util.function.Function<T, T> updateFunction) {
        T current = getObject(key, clazz);
        if (current == null) {
            return null;
        }

        T updated = updateFunction.apply(current);
        setObject(key, updated);
        return updated;
    }

    /********************** List操作 **********************/

    /**
     * 存储List
     * @param key 键
     * @param list 列表
     */
    @Override
    public <T> void setList(String key, List<T> list) {
        template.execute(ScriptConstant.LIST_SET_SCRIPT,List.of(key),list.toArray());
    }

    /**
     * 存储List并设置过期时间
     * @param key 键
     * @param list 列表
     * @param timeout 过期时间
     * @param unit 时间单位
     */
    @Override
    public <T> void setList(String key, List<T> list, long timeout, TimeUnit unit) {
        setList(key, list);
        expire(key, timeout, unit);
    }

    /**
     * 获取整个List
     * @param key 键
     * @param elementType 元素类型
     * @return 列表
     */
    @Override
    public <T> List<T> getList(String key, Class<T> elementType) {
        List<Object> rawList = listOps.range(key, 0, -1);
        return convertList(rawList, elementType);
    }

    /**
     * 获取List分页
     * @param key 键
     * @param elementType 元素类型
     * @param start 开始索引
     * @param end 结束索引
     * @return 分页列表
     */
    @Override
    public <T> List<T> getListRange(String key, Class<T> elementType, long start, long end) {
        List<Object> rawList = listOps.range(key, start, end);
        return convertList(rawList, elementType);
    }

    /**
     * 向List添加元素
     * @param key 键
     * @param element 元素
     * @return 操作后List长度
     */
    @Override
    public <T> Long addToList(String key, T element) {
        return listOps.rightPush(key, element);
    }

    /**
     * 向List添加多个元素
     * @param key 键
     * @param elements 元素集合
     * @return 操作后List长度
     */
    @Override
    public <T> Long addToList(String key, Collection<T> elements) {
        return listOps.rightPushAll(key, elements.toArray());
    }

    /**
     * 更新List中的元素
     * @param key 键
     * @param index 索引
     * @param newValue 新值
     */
    @Override
    public void updateListElement(String key, long index, Object newValue) {
        listOps.set(key, index, newValue);
    }

    /**
     * 删除List中指定值的元素
     * @param key 键
     * @param value 要删除的值
     * @param count 删除数量（>0从头开始，<0从尾开始，0全部）
     * @return 删除的数量
     */
    @Override
    public Long removeFromList(String key, Object value, long count) {
        return listOps.remove(key, count, value);
    }

    /********************** Set操作 **********************/

    /**
     * 存储Set
     * @param key 键
     * @param set 集合
     */
    @Override
    public <T> void setSet(String key, Set<T> set) {
        template.execute(ScriptConstant.SET_SET_SCRIPT,List.of(key),set.toArray());
    }

    /**
     * 存储Set并设置过期时间
     * @param key 键
     * @param set 集合
     * @param timeout 过期时间
     * @param unit 时间单位
     */
    @Override
    public <T> void setSet(String key, Set<T> set, long timeout, TimeUnit unit) {
        setSet(key, set);
        expire(key, timeout, unit);
    }

    /**
     * 获取整个Set
     * @param key 键
     * @param elementType 元素类型
     * @return 集合
     */
    @Override
    public <T> Set<T> getSet(String key, Class<T> elementType) {
        Set<Object> rawSet = setOps.members(key);
        return convertSet(rawSet, elementType);
    }

    /**
     * 向Set添加元素
     * @param key 键
     * @param elements 元素
     * @return 添加成功的数量
     */
    @Override
    public <T> Long addToSet(String key, T... elements) {
        return setOps.add(key, elements);
    }

    /**
     * 从Set移除元素
     * @param key 键
     * @param elements 元素
     * @return 移除的数量
     */
    @Override
    public Long removeFromSet(String key, Object... elements) {
        return setOps.remove(key, elements);
    }

    /**
     * 检查元素是否在Set中
     * @param key 键
     * @param element 元素
     * @return 是否存在
     */
    @Override
    public Boolean isSetMember(String key, Object element) {
        return setOps.isMember(key, element);
    }

    /********************** Hash操作 **********************/

    /**
     * 存储整个Hash
     * @param key 键
     * @param map 哈希表
     */
    @Override
    public <T> void setHashAll(String key, Map<String, T> map) {
        hashOps.putAll(key, (Map<String, ? extends Object>) map);
    }

    /**
     * 获取整个Hash
     * @param key 键
     * @param valueType 值类型
     * @return 哈希表
     */
    @Override
    public <T> Map<String, T> getHashAll(String key, Class<T> valueType) {
        Map<String, Object> rawMap = hashOps.entries(key);
        return convertMap(rawMap, valueType);
    }

    /**
     * 设置Hash字段值
     * @param key 键
     * @param field 字段
     * @param value 值
     */
    @Override
    public void setHashValue(String key, String field, Object value) {
        hashOps.put(key, field, value);
    }

    /**
     * 获取Hash字段值
     * @param key 键
     * @param field 字段
     * @param valueType 值类型
     * @return 值
     */
    @Override
    public <T> T getHashValue(String key, String field, Class<T> valueType) {
        Object value = hashOps.get(key, field);
        return convertValue(value, valueType);
    }

    /**
     * 删除Hash字段
     * @param key 键
     * @param fields 字段
     * @return 删除的数量
     */
    @Override
    public Long deleteHashFields(String key, String... fields) {
        return hashOps.delete(key, (Object[]) fields);
    }

    /**
     * 检查Hash字段是否存在
     * @param key 键
     * @param field 字段
     * @return 是否存在
     */
    @Override
    public Boolean hasHashKey(String key, String field) {
        return hashOps.hasKey(key, field);
    }

    /********************** 类型转换工具方法 **********************/

    private <T> T convertValue(Object value, Class<T> clazz) {
        if (value == null) {
            return null;
        }
        if (clazz.isInstance(value)) {
            return clazz.cast(value);
        }
        return objectMapper.convertValue(value, clazz);
    }

    private <T> List<T> convertList(List<Object> rawList, Class<T> elementType) {
        if (rawList == null) {
            return Collections.emptyList();
        }

        List<T> result = new ArrayList<>(rawList.size());
        for (Object item : rawList) {
            result.add(convertValue(item, elementType));
        }
        return result;
    }

    private <T> Set<T> convertSet(Set<Object> rawSet, Class<T> elementType) {
        if (rawSet == null) {
            return Collections.emptySet();
        }

        Set<T> result = new HashSet<>(rawSet.size());
        for (Object item : rawSet) {
            result.add(convertValue(item, elementType));
        }
        return result;
    }

    private <T> Map<String, T> convertMap(Map<String, Object> rawMap, Class<T> valueType) {
        if (rawMap == null) {
            return Collections.emptyMap();
        }

        Map<String, T> result = new HashMap<>(rawMap.size());
        for (Map.Entry<String, Object> entry : rawMap.entrySet()) {
            result.put(entry.getKey(), convertValue(entry.getValue(), valueType));
        }
        return result;
    }

    /********************** 高级操作 **********************/

    /**
     * 原子递增
     * @param key 键
     * @param delta 增量
     * @return 递增后的值
     */
    @Override
    public Long increment(String key, long delta) {
        return valueOps.increment(key, delta);
    }

    /**
     * 原子递减
     * @param key 键
     * @param delta 减量
     * @return 递减后的值
     */
    @Override
    public Long decrement(String key, long delta) {
        return valueOps.decrement(key, delta);
    }

    /**
     * 发布消息
     * @param channel 频道
     * @param message 消息
     */
    @Override
    public void publish(String channel, Object message) {
        template.convertAndSend(channel, message);
    }

    @Override
    public String generateWithTime(String prefix) throws Exception{
        return generateWithTime(prefix,8);
    }

    @Override
    public String generate(String prefix) throws Exception {
        return generate(prefix,8);
    }

    @Override
    public String generate(String prefix, int length) throws Exception {
        prefix = StringUtils.hasText(prefix) ? DEFAULT_PREFIX :prefix;
        if(length < 2){
            throw new Exception("length must be greater than 1");
        }
        RedisConnectionFactory factory = template.getConnectionFactory();
        if(factory == null){
            throw new Exception("redis connection factory is null");
        }
        RedisAtomicLong atomicLong = new RedisAtomicLong(prefix ,factory);
        String format = "%1$0" + length + "d";
        return prefix + String.format(format,atomicLong.incrementAndGet());
    }

    @Override
    public String generateWithTime(String prefix, int length) throws Exception {
        prefix = StringUtils.hasText(prefix) ? DEFAULT_PREFIX :prefix;
        if(length < 2){
            throw new Exception("length must be greater than 1");
        }
        String time = LocalDate.now().format(DateTimeFormatter.ofPattern("yyyyMMdd"));
        RedisConnectionFactory factory = template.getConnectionFactory();
        if(factory == null){
            throw new Exception("redis connection factory is null");
        }
        RedisAtomicLong atomicLong = new RedisAtomicLong(prefix + time,factory);
        String format = "%1$0" + length + "d";
        return prefix + time + String.format(format,atomicLong.incrementAndGet());
    }
}
